/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package submarines.ui;

import com.google.gson.Gson;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import submarines.*;
import submarines.Config.turnStatus;
import submarines.Main.quitAction;
import submarines.TurnManager.turnAction;
import submarines.notices.*;
import submarines.thread.EaPlayerTurn;
import submarines.thread.TimeoutChecker;

/**
 *
 * @author sshiran
 */
@WebServlet(name = "gameManager", urlPatterns = {"/WebUiGame"})
public class WebUiGame extends HttpServlet implements Ui {

    submarines.PlayerManager pManager;
    submarines.notices.NotficationManager nManager;
    Gson gsonReply;
    Status statusObj;
    EaPlayerTurn pTurn;
    TimeoutChecker executioner;
    
    public WebUiGame() {
        super();
        gsonReply = new Gson();
        Status statusObj;
        pTurn = null;
        executioner = null;
    }

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        try {
            String json = new String();
             //get same player manager that WebUi servlet used
            pManager = (PlayerManager) getServletContext().getAttribute("pManager");
            nManager = (NotficationManager) getServletContext().getAttribute("nManager"); //get same notification manager that WebUi servlet used
            pManager.startGame();
            if (pTurn == null) {
                pTurn = (EaPlayerTurn)getServletContext().getAttribute("eaTurn");
                pTurn.setFirtPlayer(pManager.get(1));
            }
            for (int i = 0; i < pManager.numPlayersLeft(); i++) {
                Player p = pManager.get(i);
                if (p.getType().equals(Config.COMPUTER_PLAYER)) {
                    OnlineEaPlayer op = (OnlineEaPlayer)p;
                    op.setTurn(pTurn);
                }
            }
            
            Reply status = new Reply();
            Player p = pManager.getPlayerBySession(request.getSession().getId().toString());
            if (p == null) {
                status.setReply("ERROR", null, "", "We are sorry, but currently you are out of the game");
            }
            if (pManager == null) {
                status.setReply("ERROR", null, null, "Player Manager not found");
                json = gsonReply.toJson(status); //prepare Gson response
                response.setContentType("text/html;charset=UTF-8");
                out.println(json);
            }
            String action = request.getParameter("actionName"); //get request

            if ("markonenemyboard".equals(action.toLowerCase())) { //mark a square on enemy board
                String enemyPlayer = request.getParameter("enemyPlayer");
                String sRow = request.getParameter("row");
                String sCol = request.getParameter("col");
                if (enemyPlayer != null && sRow != null && sCol != null && !enemyPlayer.isEmpty() && !sRow.isEmpty() && !sCol.isEmpty()) {
                    int row = Integer.parseInt(sRow);
                    int col = Integer.parseInt(sCol);
                    status = markOnEnemyBoard(request, enemyPlayer, row, col);
                }else {
                    status.setReply("ERROR", null, "returnMark", "One of the requested parameters is missing");
                }
            } else if ("unmarkonenemyboard".equals(action.toLowerCase())) { //remove marl sign from square
                String enemyPlayer = request.getParameter("enemyPlayer");
                String sRow = request.getParameter("row");
                String sCol = request.getParameter("col");
                if (enemyPlayer != null && sRow != null && sCol != null && !enemyPlayer.isEmpty() && !sRow.isEmpty() && !sCol.isEmpty()) {
                    int row = Integer.parseInt(sRow);
                    int col = Integer.parseInt(sCol);
                    status = unmarkOnEnemy(request, enemyPlayer, row, col);
                } else {
                    status.setReply("ERROR", null, "returnUnmark", "One of the requested parameters is missing");
                }
            } else if ("sendtorpedo".equals(action.toLowerCase())) { //try to hit enemy board
                //String currPlayer = request.getParameter("currPlayer");
                String enemyPlayer = request.getParameter("enemyPlayer");
                String sRow = request.getParameter("row");
                String sCol = request.getParameter("col");
                if (enemyPlayer != null && sRow != null && sCol != null &&  !enemyPlayer.isEmpty() && !sRow.isEmpty() && !sCol.isEmpty()) {
                        int row = Integer.parseInt(sRow);
                        int col = Integer.parseInt(sCol);
                        status = sendTorpedo(request, enemyPlayer, row, col);
                } else {
                    status.setReply("ERROR", null, "returnTorpedo", "One of the requested parameters is missing");
                }
            } else if ("viewenemyboard".equals(action.toLowerCase())) { //get enemy board to see status
                String currPlayer = request.getParameter("currPlayer");
                String enemyPlayer = request.getParameter("enemyPlayer");
                if (currPlayer != null && enemyPlayer != null && !currPlayer.isEmpty() && !enemyPlayer.isEmpty()) {
                    status = viewEnemyBoard(currPlayer, enemyPlayer);
                } else {
                    status.setReply("ERROR", null, "returnViewEnemyBoard", "One of the requested parameters is missing");
                }
            } else if ("quitgame".equals(action.toLowerCase())) { //player asked to quit game
                String currPlayer = request.getParameter("currPlayer");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = quitGame(request);
                } else {
                    status.setReply("ERROR", null, "returnViewEnemyBoard", "One of the requested parameters is missing");
                }
            } else if ("newgame".equals(action.toLowerCase())) { //player asked for new game
                status = (newGame());
            } else if ("restartgame".equals(action.toLowerCase())) {//player asked to restart game
                status = restartGame();
            } else if ("cleanboard".equals(action.toLowerCase())) { //remove all ships from board
                String playerName = request.getParameter("playerName");
                if (playerName != null && !playerName.isEmpty()) {
                    status = cleanBoard(playerName);
                } else {
                    status.setReply("ERROR", null, "returnBoard", "One of the requested parameters is missing");
                }
            } else if ("getplayenotices".equals(action.toLowerCase())) { //get player notices to show him on status
                String currPlayer = request.getParameter("currPlayer");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = getPlayerNotices(currPlayer);
                } else {
                    status.setReply("ERROR", null, "returnNotices", "One of the requested parameters is missing");
                }
            } else if ("getnextplayer".equals(action.toLowerCase())) { //get the player that will play this turn
                status = getNextPlayer();
            } else if ("eaplayturn".equals(action.toLowerCase())) { //get ea player play turn
                String currPlayer = request.getParameter("currPlayer");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = EaPlayTurn(currPlayer);
                } else {
                    status.setReply("ERROR", null, "returnNextPlayer", "One of the requested parameters is missing");
                }
            } else if ("getactiveplayers".equals(action.toLowerCase())) { //get the players that are still in the game

                String[] names = pManager.getPlayerNames();
                status.setReply("OK", names, "setActivePlayers", null);
            } else if ("getplayerstatus".equals(action.toLowerCase())) { //get the players that are still in the game
                String currPlayer = request.getParameter("currPlayer");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = GetPlayerStatus(currPlayer);
                } else{
                    status.setReply("ERROR", null, "returnPlayerStatus", "One of the requested parameters is missing");
                }
            } else if ("getenemytype".equals(action.toLowerCase())) { //get the players that are still in the game
                String currPlayer = request.getParameter("currPlayer");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = getEnemyType(currPlayer);
                } else{
                    status.setReply("ERROR", null, "returnGetEnemyType", "One of the requested parameters is missing");
                }
            } else if ("getenemyships".equals(action.toLowerCase())) { //get the players that are still in the game
                String currPlayer = request.getParameter("currPlayer");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = getEnemyShips(currPlayer);
                } else{
                    status.setReply("ERROR", null, "returnGetEnemyShip", "One of the requested parameters is missing");
                }
            } else if ("getgamestatus".equals(action.toLowerCase())) { //get the players that are still in the game
                if(pManager != null)
                {
                    statusObj = new Status(pManager);
                    statusObj.setStatus(pManager, request.getSession());
                    status.setReply("OK", statusObj, "parsegamestatus", "");
                }
                else{
                    status.setReply("ERROR", null, "", "There is no active game");
                }
            }
            
            if (executioner == null) {
                executioner = new TimeoutChecker(pManager, nManager);
                executioner.setName("Executioner");;
                executioner.start();
            }

            json = gsonReply.toJson(status); //prepare Gson response
            response.setContentType("text/html;charset=UTF-8");
            out.println(json); //send response
        }catch (NullPointerException e) {
            Reply res = new Reply("ERROR", null, "", "null pointer exception");
            return;
        }
        catch(IndexOutOfBoundsException e){
            Reply res = new Reply("ERROR", null, "", "OutOBounds exception");
            return;
        } finally {
            out.close();
        }
    }

    public Reply cleanBoard(String name) {
        submarines.Player currPlayer = pManager.getPlayerByName(name);
        currPlayer.myBoard.cleanBoard();//remove all ships from player board
        Reply res = new Reply("OK", currPlayer.myBoard.gameBoard, "nextMove", "Board was cleaned successfully");
        return res;
    }

    private Reply markOnEnemyBoard(HttpServletRequest request, String enemyPlayerName, int row, int col) {
        submarines.OnlineHumanPlayer currPlayer = (OnlineHumanPlayer)pManager.getPlayerBySession(request.getSession().getId().toString());
        submarines.Player enemyPlayer = pManager.getPlayerByName(enemyPlayerName);
        if(currPlayer.getName().equals(enemyPlayer.getName()))
        {
            Reply res = new Reply("ERROR", currPlayer.getName(), "returnUnmark", "You cannot mark you board");
            return res;
        }
        if (currPlayer.markOnBoard(pManager, Config.MARKED_SQUARE_SIGN, enemyPlayerName, row, col)) {
            Square test = enemyPlayer.myBoard.gameBoard[0][0];
            Reply res = new Reply("OK", currPlayer.getName(), "humanPlayTurn", "Point was marked");
            return res;
        }
        Reply res = new Reply("ERROR", currPlayer.getName(), "returnMark", "Could not mark on " + enemyPlayerName + "'s board on coordinates " + (char) ('A' + col + Config.BOARD_START) + "" + (row + (Config.BOARD_START + 1))); //if could not mark this square
        return res;
    }

    private Reply unmarkOnEnemy(HttpServletRequest request, String enemyPlayerName, int row, int col) {
        submarines.OnlineHumanPlayer currPlayer = (OnlineHumanPlayer)pManager.getPlayerBySession(request.getSession().getId().toString());
        submarines.Player enemyPlayer = pManager.getPlayerByName(enemyPlayerName);
        if(currPlayer.getName().equals(enemyPlayer.getName()))
        {
            Reply res = new Reply("ERROR", currPlayer.getName(), "returnUnmark", "You cannot unmark you board");
            return res;
        }
        if (currPlayer.markOnBoard(pManager, Config.EMPTY_SQUARE_SIGN, enemyPlayerName, row, col)) {
            Reply res = new Reply("OK", currPlayer.getName(), "humanPlayTurn", "Point was marked");
            return res;
        }
        Reply res = new Reply("ERROR", currPlayer.getName(), "returnUnmark", "Could not unmark on " + enemyPlayerName + "'s board on coordinates " + (char) ('A' + col + Config.BOARD_START) + "" + (row + (Config.BOARD_START + 1)));//if could not unmark this square
        return res;
    }
    
    private Reply sendTorpedo(HttpServletRequest request, String enemyPlayerName, int row, int col) {
        int hitResult;
        submarines.OnlineHumanPlayer currPlayer = (OnlineHumanPlayer)pManager.getPlayerBySession(request.getSession().getId().toString());
        if(!currPlayer.getName().equals(pManager.thisTurnPlayer().getName()))
        {
            Reply res = new Reply("ERROR", currPlayer.getName(), "returnTorpedo", "This is not your turn");
            return res;
        }
        submarines.Player enemyPlayer = pManager.getPlayerByName(enemyPlayerName);
        if(currPlayer.getName().equals(enemyPlayer.getName()))
        {
            Reply res = new Reply("ERROR", currPlayer.getName(), "returnTorpedo", "You cannot attack yourself");
            return res;
        }
        currPlayer.setSessionTime();
        if (currPlayer.sendTorpedo(pManager, nManager, currPlayer.getName(), enemyPlayerName, row, col)) {
            hitResult = enemyPlayer.myBoard.getHit();
            if (hitResult >= 0)  //player hit a submarine
            {
                if (hitResult > 0) { //sbmarine sunk
                    NoticePlayerSubmarineSunk sunk = new NoticePlayerSubmarineSunk(enemyPlayer, Notice.NoticeType.PLAYER_SUBMARINE_SUNK, hitResult); //create a new notice to send to all other users
                    nManager.spreadTheWord(currPlayer, enemyPlayer, sunk);//tell all other players that player submarine sunk
                    Reply res = new Reply("OK", currPlayer.getName(), "humanPlayTurn", " Submarine Sunk");
                    return res;
                }
                if(enemyPlayer.isDead()) //check if there is no more submarines to player
                {
                    pManager.playerQuit(enemyPlayer);
                    if(pManager.getMaxPlayers() == 1)
                    {
                        pManager.setMaxPlayers(-1);
                         NoticePlayerQuit q = new NoticePlayerQuit(enemyPlayer, Notice.NoticeType.PLAYER_QUIT); //create a notice that player quited
                        nManager.spreadTheWord(currPlayer, currPlayer, q); //tell all other players that player quited
                        Reply res = new Reply("OK", pManager.thisTurnPlayer().getName(), "gameOver", "Player Quit");
                        return res;
                    }else{
                        NoticePlayerQuit q = new NoticePlayerQuit(enemyPlayer, Notice.NoticeType.PLAYER_QUIT); //create a notice that player quited
                        nManager.spreadTheWord(currPlayer, currPlayer, q); //tell all other players that player quited
                        pManager.playersTurn();
                        Reply res = new Reply("OK", currPlayer.getName(), "humanPlayTurn", "Player Quit");
                        return res;
                    }
                }
                NoticePlayerGotHit n = new NoticePlayerGotHit(enemyPlayer, Notice.NoticeType.PLAYER_GOT_HIT, row, col);//create a new notice to send to all other users
                nManager.spreadTheWord(currPlayer, enemyPlayer, n);//tell all other players that player got hit
                Reply res = new Reply("OK", currPlayer.getName(), "humanPlayTurn", currPlayer.getName() + " player hit "+ enemyPlayer.getName() + " board"); //regular hit
                return res;
            } else //move to next player - player missed
            {
                pManager.playersTurn();
                NoticePlayerGotLucky q = new NoticePlayerGotLucky(enemyPlayer, Notice.NoticeType.PLAYER_GOT_LUCKY, row, col);
                nManager.spreadTheWord(currPlayer, currPlayer, q); //tell all other players that player quited
                Reply res = new Reply("OK", currPlayer.getName(), "nextPlayer", currPlayer.getName() + " player did not hit "+ enemyPlayer.getName() + " board");
                return res;
            }
        }
        Reply res = new Reply("ERROR", currPlayer.getName(), "returnTorpedo", "Could not hit on" + enemyPlayerName + "'s board on coordinates " + (char) ('A' + col + Config.BOARD_START) + "" + (row + (Config.BOARD_START + 1)));
        return res;
    }

    private Reply viewEnemyBoard(String currPlayerName, String enemyPlayerName) {
        Reply res;
        submarines.Player myPlayer = pManager.getPlayerByName(currPlayerName); 
        if (currPlayerName.toLowerCase().equals(enemyPlayerName.toLowerCase())) {//get current player board
            res = new Reply("OK", myPlayer.myBoard.gameBoard, "nextMove", "player board was sent");
        } else {
            enemyBoard eBoard = myPlayer.getEnemyBoardByName(pManager.getPlayerByName(enemyPlayerName));//get enemy player board
            if (eBoard != null) {
                res = new Reply("OK", eBoard.gameBoard, "nextMove", "player board was sent");
            } else {
                res = new Reply("ERROR", null, null, "Enemy board not found"); //could not find enemy board in competitor list
            }
        }
        return res;
    }

    private Reply quitGame(HttpServletRequest request) {
        submarines.Player currPlayer = pManager.getPlayerBySession(request.getSession().getId().toString());
        Reply res;
        pManager.playerQuit(currPlayer);
        if(pManager.numPlayersLeft() == 1) {
            pManager.setMaxPlayers(-1);
            res = new Reply("OK",  pManager.thisTurnPlayer().getName(), "gameOver", "player has left the game");
            return res;
        } else {
            res = new Reply("OK", null, "nextPlayer", "player has left the game");
            return res;
        }
    }

    public Reply newGame() {
        if (pManager.numPlayersLeft() == 1) //check that current game was endded before atarting a new one
        {
            pManager = new PlayerManager();//set new game
            getServletContext().setAttribute("pManager", (Object) pManager);//share new game with WebUi to create game
            nManager = new NotficationManager(pManager);
            Reply res = new Reply("OK", null, "newGame", "Game was started successfully");
            return res;
        } else {
            Reply res = new Reply("ERROR", null, "returnNewGame", "Game has not finished yet");
            return res;
        }
    }

    private Reply restartGame() {
        if (pManager.numPlayersLeft() == 1) {
            pManager.reInitiate(); //restart new game
            getServletContext().setAttribute("pManager", (Object) pManager);//share new game with WebUi to create game
            Reply res = new Reply("OK", null, "restartGame", "game was restarted successfully");
            return res;
        } else {
            Reply res = new Reply("ERROR", null, "returnNewGame", "Game has not finished yet");
            return res;
        }
    }

    private Reply getPlayerNotices(String playerName) //send notices to display to a player
    {
        submarines.Player currPlayer = pManager.getPlayerByName(playerName);
        LinkedList<Notice> notices = currPlayer.getNotices();
        Reply res = new Reply("OK", notices, "nextMove", "");
        return res;
    }

    private Reply getNextPlayer() { //send next player name and type 
        if (pManager != null) {
            if(pManager.numPlayersLeft() == 1)
            {
                pManager.setMaxPlayers(-1);
                Reply res = new Reply("OK",  pManager.thisTurnPlayer().getName()  , "gameOver", "");
                return res;
            }
            submarines.Player nextPlayer = pManager.thisTurnPlayer();//current player turn
            if (nextPlayer.getType().equals(Config.HUMAN_PLAYER)) {
                Reply res = new Reply("OK", nextPlayer.getName(), "humanPlayTurn", ""); 
                return res;
            } else {
                Reply res = new Reply("OK", nextPlayer.getName(), "eaPlayTurn", "");
                return res;
            }
        }
        return null;
    }

    private Reply EaPlayTurn(String currPlayerName) {
        int row;
        int col;
        submarines.Player enemyPlayer;
        submarines.EaPlayer currPlayer = (submarines.EaPlayer) pManager.getPlayerByName(currPlayerName);
        String result[];
        turnStatus playSingleTurn = currPlayer.playSingleTurn(pManager, nManager);
        result = currPlayer.getEaMoves();
        enemyPlayer = pManager.getPlayerByName(result[0]);
        row = Integer.parseInt(result[1]);
        col = Integer.parseInt(result[2]);
        if (playSingleTurn == Config.turnStatus.SHIP_SANK) 
        {
            Reply res = new Reply("OK",  currPlayer.getEaMoves(), "eaPlayTurn", " Submarine Sunk");
            return res;
        } else if (playSingleTurn == Config.turnStatus.PLAYER_LOST) {//check if player have more submarines
            Reply res = new Reply("OK",  currPlayer.getEaMoves(), "eaPlayTurn", "Player Quit");
            return res;
        } else if(playSingleTurn == Config.turnStatus.HIT) {
            Reply res = new Reply("OK",  currPlayer.getEaMoves(), "eaPlayTurn", currPlayerName + " player hit "+ enemyPlayer.getName() + " board"); //regular hit
            return res;
        } else if(playSingleTurn == Config.turnStatus.MISSED) {
            pManager.getNextPlayer();
            Reply res = new Reply("OK", currPlayer.getEaMoves(), "nextPlayer", currPlayerName + " player did not hit "+ enemyPlayer.getName() + " board");
            return res;
        } else {
            Reply res = new Reply("ERROR", null, "eaPlayTurn", "Player can't play turn");
            return res;
        }
    }
    
    private Reply GetPlayerStatus(String currPlayerName) {
        submarines.Player currPlayer = pManager.getPlayerByName(currPlayerName);
        if(currPlayer.isDead())
        {
            Reply res = new Reply("OK", "Dead", "setPlayerStatus", "");
            return res;            
        }
        else
        {
            Reply res = new Reply("OK", "Alive", "setPlayerStatus", "");
            return res;            
        }
    }
    
    private Reply getEnemyType(String currPlayerName) {
        submarines.Player currPlayer = pManager.getPlayerByName(currPlayerName);
        if(currPlayer.getType().equals(Config.COMPUTER_PLAYER))
        {
            Reply res = new Reply("OK",  "Human", "setEnemyType", "Human");
            return res;            
        }
        else
        {
            Reply res = new Reply("OK",  "Computer", "setEnemyType", "Computer");
            return res;            
        }
    }
    
private Reply getEnemyShips(String currPlayerName) {
        submarines.Player currPlayer = pManager.getPlayerByName(currPlayerName);
        String[][] subString = Config.cloneAliases();
        int[] remainingSubs = currPlayer.myBoard.getRemainingSubsGame().clone();
        int[] initial = Config.SUBMARINES.clone();
        
        for (int i = 0; i < currPlayer.myBoard.subs.length; i++) {
            for (int j = remainingSubs[i]; j < initial[i]; j++) {
                subString[i][j] = ""; //clear sub from list if player hit is alredy
            }
        }
        Reply res = new Reply("OK", subString, "setSubStatus", "");
        return res;
    }

    @Override
    public String getMissleCoords() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public quitAction getNewGameAswer() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getSubmarineDirection(int subSize) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getSubmarineRow(int subSize, int minRow, int maxRow) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getSubmarineCol(int subSize, int minCol, int maxCol) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public turnAction getPlayersNextMove() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getTargetedPlayer(LinkedList<String> list) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getNumberOfPlayers() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public playerType getPlayerType() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getPlayerName() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean addAnotherPlayer() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printAlert(String msg) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printBoard(Board toPrint) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void clearScreen() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printEnemyBoard(enemyBoard board) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String askUserYesNoQuestions(String question) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printComputerMove(String attacker, enemyBoard board, int row, int col) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
}
